package oneweone.com.copyapp;

import android.Manifest;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private PackageManager mPackageManager;
    private Button mBtnAllApps;
    private Button mBtnSystemApps;
    private Button mBtnDataApps;
    private ListView mListView;
    private AppListAdapter mAdapter;
    public int MID;
    private ProgressDialog mLoadingDialog;

    private static final String BACKUP_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
    private static final String APK = ".apk";
    private static final String ODEX = ".odex";
    private static final String OAT32 = "oat/arm";
    private static final String OAT64 = "oat/arm64";

    public static enum AppType {
        SYSTEM, DATA, ALL
    }

    private static final int EVENT_COMPLETE = 1;
    private Handler mH = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_COMPLETE:
                    Toast.makeText(MainActivity.this, (String) msg.obj,
                            Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        mListView.setOnCreateContextMenuListener(this);

        mBtnAllApps = (Button) findViewById(R.id.all_app_btn);
        mBtnSystemApps = (Button) findViewById(R.id.system_app_btn);
        mBtnDataApps = (Button) findViewById(R.id.installed_app_btn);

        mBtnAllApps.setOnClickListener(this);
        mBtnSystemApps.setOnClickListener(this);
        mBtnDataApps.setOnClickListener(this);

        mPackageManager = getPackageManager();

        mLoadingDialog = new ProgressDialog(this);
        mLoadingDialog.show();
        new Thread() {
            @Override
            public void run() {
                final List<PackageInfo> lists = mPackageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
                Collections.sort(lists, new DisplayNameComparator(mPackageManager));
                mH.post(new Runnable() {
                    @Override
                    public void run() {
                        mAdapter = new AppListAdapter(MainActivity.this, lists);
                        mListView.setAdapter(mAdapter);
                        mLoadingDialog.cancel();
                    }
                });
            }
        }.start();

        checkSdCardPermission();

    }

    @Override
    public void onClick(View view) {
        // TODO Auto-generated method stub
        if (view == mBtnAllApps) {
            if (mAdapter.getAppType() != AppType.ALL) {
                mAdapter.setAppType(AppType.ALL);
                mAdapter.notifyDataSetChanged();
            }
        } else if (view == mBtnSystemApps) {
            if (mAdapter.getAppType() != AppType.SYSTEM) {
                mAdapter.setAppType(AppType.SYSTEM);
                mAdapter.notifyDataSetChanged();
            }
        } else if (view == mBtnDataApps) {
            if (mAdapter.getAppType() != AppType.DATA) {
                mAdapter.setAppType(AppType.DATA);
                mAdapter.notifyDataSetChanged();
            }
        }
    }

//    @Override
//    public void onCreateContextMenu(ContextMenu menu, View v,
//                                    ContextMenu.ContextMenuInfo menuInfo) {
//        super.onCreateContextMenu(menu, v, menuInfo);
//    }
//
//    @Override
//    public boolean onContextItemSelected(MenuItem item) {
//        // TODO Auto-generated method stub
//        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
//        MID = (int) info.id;
//        PackageInfo pi = mAdapter.getItem(MID);
//        switch (item.getItemId()) {
//            case R.id.backup:
//                backupApp(pi);
//                return true;
//        }
//        return super.onContextItemSelected(item);
//    }

    private Drawable getAppIcon(PackageInfo packageInfo) {
        return packageInfo.applicationInfo.loadIcon(mPackageManager);
    }

    private String getAppName(PackageInfo packageInfo) {
        return packageInfo.applicationInfo.loadLabel(mPackageManager)
                .toString();
    }

    private String getAppPackageName(PackageInfo packageInfo) {
        return packageInfo.packageName;
    }

    private String getAppSourceDir(PackageInfo packageInfo) {
        return packageInfo.applicationInfo.sourceDir;
    }

    private void backupApp(PackageInfo packageInfo) {
        String source = getAppSourceDir(packageInfo);
        if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
            String key = getAppName(packageInfo);
            String dest = BACKUP_PATH + key + APK;
            Toast.makeText(this, R.string.backup_start, Toast.LENGTH_SHORT).show();
            new Thread(new CopyRunnable(source, dest, key)).start();
        } else {
            String path = new File(source).getParent();
            String name = new File(source).getName();
            name = name.substring(0, name.indexOf(APK));

            String kitKat = new File(path).getParent() + File.separator + name + ODEX;
            String oat32 = path + File.separator + OAT32 + File.separator + name + ODEX;
            String oat64 = path + File.separator + OAT64 + File.separator + name + ODEX;

//            if (!new File(kitKat).exists() && !new File(oat32).exists()
//                    && !new File(oat64).exists()) {
                String key = getAppName(packageInfo);
                if (getAppIcon(packageInfo).equals(
                        getAppPackageName(packageInfo))) {
                    key = name;
                }
                String dest = BACKUP_PATH + key + APK;
                Toast.makeText(this, R.string.backup_start, Toast.LENGTH_SHORT).show();
                new Thread(new CopyRunnable(source, dest, key)).start();
//            } else {
//                Toast.makeText(this, R.string.apk_has_oat, Toast.LENGTH_SHORT).show();
//            }
        }
    }

    private class CopyRunnable implements Runnable {

        private String source;
        private String dest;
        private String key;

        public CopyRunnable(String source, String dest, String key) {
            this.source = source;
            this.dest = dest;
            this.key = key;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                int length = 1024 * 1024;

                if (!new File(BACKUP_PATH).exists()) {
                    new File(BACKUP_PATH).mkdirs();
                }

                File fDest = new File(dest);
                if (fDest.exists()) {
                    fDest.delete();
                }
                fDest.createNewFile();

                FileInputStream in = new FileInputStream(new File(source));
                FileOutputStream out = new FileOutputStream(fDest);
                FileChannel inC = in.getChannel();
                FileChannel outC = out.getChannel();
                int i = 0;
                while (true) {
                    if (inC.position() == inC.size()) {
                        inC.close();
                        outC.close();
                        Message message = mH.obtainMessage(EVENT_COMPLETE);
                        message.obj = MainActivity.this.getString(R.string.backup_success, key, dest);
                        mH.sendMessage(message);
                        break;
                    }
                    if ((inC.size() - inC.position()) < 1024 * 1024) {
                        length = (int) (inC.size() - inC.position());
                    } else {
                        length = 1024 * 1024;
                    }
                    inC.transferTo(inC.position(), length, outC);
                    inC.position(inC.position() + length);
                    i++;
                }
            } catch (Exception e) {
                // TODO: handle exception
                Log.e("zhouyj", e.toString());
            }

        }
    }

    private class AppListAdapter extends BaseAdapter {

        private Context context;
        private List<PackageInfo> lists;
        private AppType appType = AppType.DATA;

        public AppListAdapter(Context context, List<PackageInfo> lists) {
            this.context = context;
            this.lists = lists;
        }

        public AppType getAppType() {
            return appType;
        }

        public void setAppType(AppType appType) {
            this.appType = appType;
        }

        private List<PackageInfo> getDataList(AppType appType) {
            List<PackageInfo> l = new ArrayList<PackageInfo>();
            switch (appType) {
                case SYSTEM:
                    for (PackageInfo pi : lists) {
                        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                            l.add(pi);
                        }
                    }
                    break;
                case DATA:
                    for (PackageInfo pi : lists) {
                        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                            l.add(pi);
                        }
                    }
                    break;
                case ALL:
                    return lists;
            }
            return l;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return getDataList(getAppType()).size();
        }

        @Override
        public PackageInfo getItem(int position) {
            // TODO Auto-generated method stub
            return getDataList(getAppType()).get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            final PackageInfo pi = getItem(position);

            ViewHolder viewHolder = null;
            if (convertView == null) {
                viewHolder = new ViewHolder();
                LayoutInflater mInflater = LayoutInflater.from(context);
                convertView = mInflater.inflate(R.layout.item_app, null);

                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.app_icon_iv);
                viewHolder.tvAppName = (TextView) convertView.findViewById(R.id.app_name_tv);
                viewHolder.tvPackageName = (TextView) convertView.findViewById(R.id.app_packagename_tv);
                viewHolder.tvPath = (TextView) convertView.findViewById(R.id.app_path_tv);

                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.imageView.setImageDrawable(getAppIcon(pi));
            viewHolder.tvAppName.setText(getAppName(pi));
            viewHolder.tvPackageName.setText(getAppPackageName(pi));
            viewHolder.tvPath.setText(getAppSourceDir(pi));

            convertView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    backupApp(pi);
                    return false;
                }
            });

            return convertView;
        }
    }

    private static class ViewHolder {
        ImageView imageView;
        TextView tvAppName;
        TextView tvPackageName;
        TextView tvPath;
    }

    public static class DisplayNameComparator implements
            Comparator<PackageInfo> {
        public DisplayNameComparator(PackageManager pm) {
            mPM = pm;
            mCollator.setStrength(Collator.PRIMARY);
        }

        public final int compare(PackageInfo a, PackageInfo b) {
            // We want to put the one targeted to another user at the end of the
            // dialog.
            CharSequence sa = a.applicationInfo.loadLabel(mPM);
            if (sa == null)
                sa = a.packageName;
            CharSequence sb = b.applicationInfo.loadLabel(mPM);
            if (sb == null)
                sb = b.packageName;

            return mCollator.compare(sa.toString(), sb.toString());
        }

        private final Collator mCollator = Collator.getInstance();
        private PackageManager mPM;
    }

    public void checkSdCardPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
    }
}
